package com.example.demo.pojo;

import java.util.ArrayList;
import java.util.Random;

/**
 * @program:IntelliJ IDEA
 * @Description:基因组
 * @author:Mr.CheCheHuiBujianO
 * @data: 2023/5/24 16:31
 */
public class GAGroup {
    public ArrayList<Gene> group;//种群，染色体集
    //动态数组记录状态
    public ArrayList<ArrayList<Integer>> check;
    //check需要是二维数组来确认整个种群上的染色体点位
    public ArrayList<ArrayList<Integer>> workStatus;//状态，最开始都为待机wait，除了偏好员工外
    public ArrayList<ArrayList<Integer>> workingTime;//0下标表示连续工作时间，1下标表示日工作时间 2下标表示周工作时间


    private double pm = 0.5;//变异概率，//一般以上概率不能手动改变，只能程序员改代码
    private int len = 32;//染色体长度，需要传入上班前工作时间，下班后工作时间
    private int size;//可上班员工数目，种群规模

    public void setLen(int len) {
        this.len = len;
    }


    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public GAGroup(ArrayList<Gene> group) {
        this.group = group;


    }//

    /**
     * 某个班次的上班人数
     */
    private int countPopulations(int index) {
        int res = 0;
        for (int i = 0; i < size; i++) {
            if (group.get(i).geneList.get(index) == 1) res++;
        }
        return res;//多少个人
    }//

    /**
     * 持续上班时间
     */
    private int persistentTime(int index) {//持续时间
        return workingTime.get(index).get(0);
    }//

    /**
     * 日工作时间
     */
    private int DayTime(int index) {
        return workingTime.get(index).get(1);
    }//

    private void resetPerTime(int index)//下班后重置
    {
        ArrayList<Integer> tmp = workingTime.get(index);
        tmp.set(0, 0);//持续时间重置0
        workingTime.set(index, tmp);
    }

   

    /**
     * @Description: preNum工作前需要多少人进行打扫活动
     * freeNum
     * afterNum
     * passengerFlowList 在某一时间段中需要多少个人进行值班活动
     * pre
     * loves_arr 偏好数组
     * @Param: [preNum, freeNum, afterNum, passengerFlowList, pre, later, loves_arr]
     * @return:boolean
     * @Author: Mr.Lin
     * @Date: 2023/3/9 0:42
     */
    private boolean fitness(int preNum, int freeNum, int afterNum, ArrayList<Double> passengerFlowList, int pre, int later) {
        //如果现在len是28，代表 pre 24 later的一个时间构造，解析上班时间的时候，就需要传上下班这个参数来确定

        Random r = new Random();

        /*
        将种群中每一个人的基因型进行初始化
         */
        for (int i = 0; i < group.size(); i++) {
            Gene tmp = group.get(i);
            for (int j = 0; j < len; j++) {
                tmp.geneList.set(j, 0);
            }
            group.set(i, tmp);
        }
        //每次排班重置group，不然都是一样的

        /*
        preNum是开店前的需要员工数量的参数
         */
        while (countPopulations(0) != preNum) {//preNum个人工作前打扫
            mutate(0);//变异第一列
        }

        //需参数设置初始化
        for (int i = 1; i < pre; i++) {
            for (int j = 0; j < size; j++) {
                Gene tmp = group.get(j);
                tmp.geneList.set(i, tmp.geneList.get(i - 1));
                group.set(j, tmp);
            }
        }


        //确认第一行后进行check，workStatus，workingTime的修改

        /*
        tmp首先接收了某人的染色体
        然后将
         */
        for (int i = 0; i < pre; i++) {
            for (int j = 0; j < size; j++) {//j需要参数
                if (group.get(j).geneList.get(i) == 1) {//如果c第一班次被选中
                    ArrayList<Integer> tmp = check.get(j);
                    tmp.set(i, 1);//check 1代表确认
                    check.set(i, tmp);
                    tmp = workStatus.get(j);
                    tmp.set(i, 2);//workStatus 2代表上班中
                    workStatus.set(j, tmp);
                    tmp = workingTime.get(j);
                    tmp.set(0, persistentTime(j) + 1);
                    tmp.set(1, DayTime(j) + 1);
                    workingTime.set(j, tmp);
                }
                //虽然其他的未被确定，但是也用不上了，只需要确认需要上班的即可
            }
        }
        //第一班次设完之后，后面就是人数多，那么如果时间没<8,就上  人数少，那么随机挑选持续时间<8的n个人上
        //大循环
        //int ran=0;
        for (int i = pre; i < len; i++) {
            //ran++;
            double PopulationsNeed = 0;
            if (i >= pre && i < len - later) PopulationsNeed = passengerFlowList.get(i - pre);
            else if (i >= len - later) PopulationsNeed = afterNum;

            if ((int) PopulationsNeed == 0) PopulationsNeed = freeNum;//没有人时需要1人
            //正常休息
            for (int j = 0; j < size; j++) {//j是人，i是位点
                if (persistentTime(j) >= 8) {
                    ArrayList<Integer> tmp = check.get(j);
                    tmp.set(i, 1);//确认
                    if (i + 1 < size)
                        tmp.set(i + 1, 1);
                    check.set(j, tmp);
                    tmp = workStatus.get(j);
                    tmp.set(i, 3);//休息中
                    if (i + 1 < size)
                        tmp.set(i + 1, 3);//休息
                    workStatus.set(j, tmp);
                    resetPerTime(j);//重置这个人的连续工作时间
                }//安排休息没问题

            }
            //首先是确定需要的人数
            if (countPopulations(i - 1) <= (int) Math.ceil(PopulationsNeed)) {//还需要人上班，可以上的都上去
                for (int j = 0; j < size; j++) {//循环每一个
                    if (persistentTime(j) > 0 && persistentTime(j) < 8 && group.get(j).geneList.get(i - 1) == 1 && workStatus.get(j).get(i) == 1) {//等待上班，并且可以上
                        Gene work = group.get(j);
                        work.geneList.set(i, 1);
                        group.set(j, work);

                        ArrayList<Integer> tmp = check.get(j);
                        tmp.set(i, 1);//确认上班
                        check.set(j, tmp);

                        tmp = workStatus.get(j);
                        tmp.set(i, 2);//上班中
                        workStatus.set(j, tmp);

                        tmp = workingTime.get(j);
                        tmp.set(0, persistentTime(j) + 1);
                        tmp.set(1, DayTime(j) + 1);
                        workingTime.set(j, tmp);
                    }
                }


                //还缺多少人，随机上
                while ((int) Math.ceil(PopulationsNeed) != countPopulations(i)) {//人不够,随机选在wait的人上班,每次选一个
                    int index = Math.abs(r.nextInt() % size);
                    if (check.get(index).get(i) == 0 && workStatus.get(index).get(i) == 1 && DayTime(index) < 16) {//满足要求，上班
                        Gene work = group.get(index);
                        work.geneList.set(i, 1);
                        group.set(index, work);

                        ArrayList<Integer> tmp = check.get(index);
                        tmp.set(i, 1);//确认上班
                        check.set(index, tmp);
                        tmp = workStatus.get(index);
                        tmp.set(i, 2);//上班中
                        workStatus.set(index, tmp);
                        tmp = workingTime.get(index);
                        tmp.set(0, persistentTime(index) + 1);
                        tmp.set(1, DayTime(index) + 1);
                        workingTime.set(index, tmp);
                    }
                }
                //已凑上
            }
            //决定执行工作时长<2时就算人太多也不下班
            else if (countPopulations(i - 1) > (int) Math.ceil(PopulationsNeed)) {
                for (int j = 0; j < size; j++) {
                    if (group.get(j).geneList.get(i - 1) == 1 && persistentTime(j) < 4 && persistentTime(j) > 0) {
                        Gene work = group.get(j);
                        work.geneList.set(i, 1);
                        group.set(j, work);

                        ArrayList<Integer> tmp = check.get(j);
                        tmp.set(i, 1);//确认上班
                        check.set(j, tmp);

                        tmp = workStatus.get(j);
                        tmp.set(i, 2);//上班中
                        workStatus.set(j, tmp);

                        tmp = workingTime.get(j);
                        tmp.set(0, persistentTime(j) + 1);
                        tmp.set(1, DayTime(j) + 1);
                        workingTime.set(j, tmp);
                    }
                }
                while (countPopulations(i) < (int) Math.ceil(PopulationsNeed)) {
//                    System.out.println(countPopulations(i) + " " + (int) Math.ceil(PopulationsNeed) + " " + 2);
                    int index = Math.abs(r.nextInt() % size);
                    if (check.get(index).get(i) == 0 && workStatus.get(index).get(i) == 1 && DayTime(index) < 16 && persistentTime(index) < 8) {
                        //index上班，其他的下班(重置持续时间)
                        Gene work = group.get(index);
                        work.geneList.set(i, 1);
                        group.set(index, work);

                        ArrayList<Integer> tmp = check.get(index);
                        tmp.set(i, 1);//确认上班
                        check.set(index, tmp);

                        tmp = workStatus.get(index);
                        tmp.set(i, 2);//上班中
                        workStatus.set(index, tmp);

                        tmp = workingTime.get(index);
                        tmp.set(0, persistentTime(index) + 1);
                        tmp.set(1, DayTime(index) + 1);
                        workingTime.set(index, tmp);
                    }
                }
                for (int j = 0; j < size; j++) {
                    if (check.get(j).get(i) == 0) {
                        resetPerTime(j);//重置它的持续时间
                    }
                }

            }
        }
        return true;
    }

    //变异，变异概率pm
    //对某一点位全部随机变异
    private void mutate(int pos) {
        //取反即可
        Random r = new Random();
        for (int i = 0; i < size; i++) {//

            if (Math.abs(r.nextInt() % 10000 / 10000.0) < pm && check.get(i).get(pos) == 0 && workStatus.get(i).get(pos) == 1) {//未确认，在waiting才能变异上班,并且满足概率
                if (group.get(i).geneList.get(pos) == 0) {
                    Gene chro = group.get(i);
                    chro.geneList.set(pos, 1);
                    group.set(i, chro);
                } else {
                    Gene chro = group.get(i);
                    chro.geneList.set(pos, 0);
                    group.set(i, chro);
                }
            }
        }
    }


    private void init() {
        //初始化check
        check = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            ArrayList<Integer> tmp = new ArrayList<>();
            for (int j = 0; j < len; j++) {
                tmp.add(0);
            }
            check.add(tmp);
        }

        //初始化状态workStatus
        workStatus = new ArrayList<>();
        //1代表waiting 准备上班
        //2代表working 上班中
        //3代表resting 休息中/吃饭中，这个时候不能派班
        for (int i = 0; i < size; i++) {
            ArrayList<Integer> tmp = new ArrayList<>();
            for (int j = 0; j < len; j++) {
                tmp.add(1);
            }
            workStatus.add(tmp);
        }

        //初始化工作时间workingTime
        workingTime = new ArrayList<>();
        //0下标表示连续工作时间，1下标表示日工作时间 2下标表示周工作时间
        for (int i = 0; i < size; i++) {
            ArrayList<Integer> tmp = new ArrayList<>();
            for (int j = 0; j < 2; j++) {
                tmp.add(0);
            }
            workingTime.add(tmp);
        }
    }

    //GA算法主体
    public ArrayList<Gene> GA(int preNum, int freeNum, int afterNum, ArrayList<Double> passengerFlowList, int pre, int later, int day) {
        ArrayList<Gene> groupTmp1 = group;
        ArrayList<Gene> groupTmp2 = group;
        //员工开始的随机时间安排
        for (int i = 0; i < size; i++) {
            System.out.println("员工" + i);
            for (int j = 0; j < len; j++) {
                System.out.print(group.get(i).geneList.get(j));
                System.out.print(" ");
            }
            System.out.println();
        }

        init();
        //1.初始化种群
        //2.while次迭代以全局适应
        Random r = new Random();

        while (fitness(preNum, freeNum, afterNum, passengerFlowList, pre, later) != true)//while循环中一直在进行种群更新
        {
            //在fitness中进行变异
        }

        //完成GA，显示结果
        System.out.println("Day" + day + "已完成排班，结果如下：");
        for (int i = 0; i < size; i++) {
            System.out.println("员工" + i);
            for (int j = 0; j < len; j++) {
                System.out.print(group.get(i).geneList.get(j).intValue());
                System.out.print(" ");
            }
            System.out.println();
        }
        System.out.println("结果显示完成!!!");
        groupTmp1 = group;
        group = groupTmp2;
        return groupTmp1;
    }
}
